﻿using System;
using System.Collections.Generic;
using System.Diagnostics.Contracts;

namespace GBricks.Collections
{
  public static class Pair
  {
    public static KeyValuePair<TKey, TValue> Create<TKey, TValue>(TKey key, TValue value) {
      return new KeyValuePair<TKey, TValue>(key, value);
    }

    public static IEqualityComparer<KeyValuePair<TKey, TValue>> Comparer<TKey, TValue>(IEqualityComparer<TKey> key = null, IEqualityComparer<TValue> value = null) {
      return new PairEqualityComparer<TKey, TValue>(key, value);
    }

    [Serializable]
    private sealed class PairEqualityComparer<TKey, TValue> : EqualityComparer<KeyValuePair<TKey, TValue>>
    {
      public PairEqualityComparer(IEqualityComparer<TKey> key = null, IEqualityComparer<TValue> value = null) {
        Key = key ?? EqualityComparer<TKey>.Default;
        Value = value ?? EqualityComparer<TValue>.Default;
      }

      private IEqualityComparer<TKey> Key { get; set; }
      private IEqualityComparer<TValue> Value { get; set; }

      [ContractInvariantMethod]
      private void Invariant() {
        Contract.Invariant(Key != null);
        Contract.Invariant(Value != null);
      }

      public override bool Equals(KeyValuePair<TKey, TValue> x, KeyValuePair<TKey, TValue> y) {
        return Key.Equals(x.Key, y.Key) && Value.Equals(x.Value, y.Value);
      }

      public override int GetHashCode(KeyValuePair<TKey, TValue> obj) {
        return HashCode.Combine(Key.GetHashCode(obj.Key), Value.GetHashCode(obj.Value));
      }

      public override bool Equals(object obj) {
        var other = obj as PairEqualityComparer<TKey, TValue>;
        return other != null && Equals(other.Key, Key) && Equals(other.Value, Value);
      }

      public override int GetHashCode() {
        return HashCode.Calculate(Key, Value);
      }
    }
  }
}
